/*
 * Copyright (c) 2024 endless-sky
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#pragma once

#include <atomic>
#include <memory>
#include <string>
#include <unordered_map>
#include <mutex>
#include <thread>
#include "utils_noncopyable.h"
#include "nlohmann/json.hpp"
#include "record_search.h"

namespace El {
namespace Record {

class RecordSearchCache {
    DISALLOW_COPY_AND_MOVE(RecordSearchCache);

public:
    static RecordSearchCache &GetInstance();

    /**
     * @brief 启动搜索管理器
     * @return true 成功，false 失败
     */
    bool Start();

    /**
     * @brief 停止搜索管理器
     */
    void Stop();

    /**
     * @brief 搜索视频文件
     * @param ch 通道号
     * @param start 开始时间(UTC秒)
     * @param end 结束时间(UTC秒)
     * @return 符合条件的视频文件列表
     */
    std::vector<VideoFileInfo> SearchVideo(int32_t ch, TimePoint start, TimePoint end);

    /**
     * @brief 搜索图片文件
     * @param ch 通道号
     * @param start 开始时间(UTC秒)
     * @param end 结束时间(UTC秒)
     * @return 符合条件的图片文件列表
     */
    std::vector<ImageFileInfo> SearchImage(int32_t ch, TimePoint start, TimePoint end);

    /**
     * @brief 添加新的视频文件信息到索引缓存
     * @param info 视频文件信息(JSON格式)
     * @return true 成功，false 失败
     */
    bool AddVideoFile(const nlohmann::json& info);

    /**
     * @brief 添加新的图片文件信息到索引缓存
     * @param info 图片文件信息(JSON格式)
     * @return true 成功，false 失败
     */
    bool AddImageFile(const nlohmann::json& info);

    /**
     * @brief 删除最老的文件（视频或图片）
     * @return true 成功删除文件，false 没有找到文件或删除失败
     */
    bool DeleteOldestFile();

private:
    RecordSearchCache();
    ~RecordSearchCache() = default;

    /**
     * @brief 用于删除文件的辅助结构体
     */
    struct FileToDelete {
        TimePoint time;      // 文件时间
        std::string path;    // 文件完整路径
        bool isVideo;        // 是否是视频文件
        std::string date;    // 日期(YYYYMMDD格式)
        std::string filename;// 文件名
    };

    /**
     * @brief 加载指定日期的索引文件
     * @param date 日期(YYYYMMDD格式)
     */
    void LoadDayIndex(const std::string &date);

    /**
     * @brief 清理过期的索引缓存
     */
    void CleanExpiredCache();

    /**
     * @brief 监控索引文件变化的线程函数
     */
    void MonitorThread();

    /**
     * @brief 加载所有索引文件
     */
    void LoadAllIndices();

    /**
     * @brief 扫描指定目录下的所有文件，并更新索引
     * @param date 日期(YYYYMMDD格式)
     * @param dayPath 日期目录路径
     */
    void ScanDirectory(const std::string &date, const std::string &dayPath  );

    /**
     * @brief 从索引中删除文件记录
     * @param file 要删除的文件信息
     * @param modifiedIndices 记录被修改的索引
     */
    void RemoveFileFromIndex(const FileToDelete &file, 
        std::unordered_map<std::string, nlohmann::json> &modifiedIndices);

    /**
     * @brief 处理文件删除及相关操作
     * @param file 要删除的文件信息
     * @param modifiedIndices 记录被修改的索引
     * @param isFileNotFound 是否是因为文件未找到而调用
     * @return true 成功处理，false 处理失败
     */
    bool HandleFileDelete(const FileToDelete &file,
        std::unordered_map<std::string, nlohmann::json> &modifiedIndices,
        bool isFileNotFound);

    /**
     * @brief 保存修改过的索引文件
     * @param date 日期(YYYYMMDD格式)
     * @param index 需要保存的索引数据
     * @return 是否保存成功
     */
    bool SaveModifiedIndex(const std::string &date, const nlohmann::json &index);

private:
    std::string storePath_{"/mnt/udisk"};                  // 存储根路径
    std::mutex cacheMutex_;                                 // 缓存互斥锁
    std::unordered_map<std::string, nlohmann::json> cache_; // 索引缓存，key为日期(YYYYMMDD)
    std::atomic<bool> running_{false};                      // 运行标志
    std::unique_ptr<std::thread> monitorThread_;            // 监控线程
};

} // namespace Record
} // namespace El